package urlqueue

import (
	"time"

	"github.com/pkg/errors"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

// urlQueueModel 数据库表结构
type urlQueueModel struct {
	ID         uint64
	CreatedAt  time.Time
	UpdatedAt  time.Time
	RawURL     string `gorm:"VARCHAR(255);not null;unique"`
	Referer    string `gorm:"VARCHAR(255)"`
	WasVisited bool
}

func (m urlQueueModel) TableName() string { return "url_queue" }

// queueInDBS 一个URL列队，url存储在数据库中
type queueInDBS struct {
	db *gorm.DB
}

// NewQueueInDBS 创建一个存储在数据库中的URL列队
func NewQueueInDBS(db *gorm.DB) (URLQueue, error) {
	if err := db.AutoMigrate(new(urlQueueModel)); err != nil {
		return nil, err
	}

	return &queueInDBS{db}, nil
}

// Push
func (q *queueInDBS) Push(rawurl, refererUrl string) error {
	myURL, err := ParseMyURL(rawurl, refererUrl)
	if err != nil {
		return nil
	}

	// 有冲突的时候，什么也不做
	result := q.db.Clauses(clause.OnConflict{DoNothing: true}).Create(
		&urlQueueModel{RawURL: myURL.String(), Referer: refererUrl},
	)
	return result.Error
}

// ForcePush
func (q *queueInDBS) ForcePush(myURL *MyURL) error {
	if myURL == nil {
		return nil
	}
	result := q.db.Model(new(urlQueueModel)).Where("raw_url=?", myURL.String()).Update("was_visited", false)
	return result.Error
}

// Pop
func (q *queueInDBS) Pop() (*MyURL, error) {
	model := new(urlQueueModel)
	result := q.db.Where("was_visited=?", false).Order("updated_at desc, created_at desc").First(model)
	if result.Error != nil {
		if errors.Is(result.Error, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, result.Error
	}

	upResult := q.db.Model(new(urlQueueModel)).
		Where("id=? and raw_url=?", model.ID, model.RawURL).
		Update("was_visited", true)
	if upResult.Error != nil {
		return nil, upResult.Error
	}

	return ParseMyURL(model.RawURL, model.Referer)
}

// GetQueueLen
func (q *queueInDBS) GetQueueLen() (int64, error) {
	var num int64
	result := q.db.Model(new(urlQueueModel)).Where("was_visited=?", false).Count(&num)
	return num, result.Error
}

// GetTotalNumber
func (q *queueInDBS) GetTotalNumber() (int64, error) {
	var num int64
	result := q.db.Model(new(urlQueueModel)).Count(&num)
	return num, result.Error
}
